using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Threading;

namespace AnguaRPGEngine.Library.Events
{
    public class EventAggregator : IEventAggregator
    {
        private readonly object syncRoot = new object();
        private readonly List<object> registeredListeners = new List<object>();
        private readonly SynchronizationContext synchronizationContext;

        public EventAggregator()
        {
            if (SynchronizationContext.Current == null)
                SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());

            synchronizationContext = SynchronizationContext.Current;

        }

        public void RegisterListener(object objectToRegister)
        {
            WhileLocked(() =>
                            {
                                if (registeredListeners.Contains(objectToRegister))
                                    return;

                                registeredListeners.Add(objectToRegister);

                            });

        }

        public void SendMessage<T>() where T : new()
        {
            SendMessage(new T());

        }

        public void SendMessage<T>(T message)
        {
            SendAction(() =>
                           {
                               foreach (var listener in 
                                   AllRegisteredListeners().OfType<IHandleMessagesOfType<T>>())
                               {
                                   listener.HandleMessage(message);
                               }

                           }
                );
        }

        public void UnregisterListener(object objectToUnregister)
        {
            WhileLocked(() =>
                            {
                                if (registeredListeners.Contains(objectToUnregister))
                                    registeredListeners.Remove(objectToUnregister);
                            }
                );

        }

        public void SendAction(Action action)
        {
            synchronizationContext.Send(state => action(), null);

        }

        private void WhileLocked(Action action)
        {
            lock (syncRoot)
                action();

        }

        private IEnumerable<object> AllRegisteredListeners()
        {
            lock (syncRoot)
                return registeredListeners.ToArray();

        }
    }
}